package com.ly.mp.busicen.rule.flow.filter.plugin;

import static com.ly.mp.busicen.rule.XruleStrUtils.splitJKH;
import static com.ly.mp.busicen.rule.XruleStrUtils.splitParam;
import static com.ly.mp.busicen.rule.XruleStrUtils.splitXKH;

import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.collections4.IteratorUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.ly.mp.busicen.rule.flow.FlowSpelUtil;
import com.ly.mp.busicen.rule.flow.IDataVolume;
import com.ly.mp.busicen.rule.flow.IFlowVolume;
import com.ly.mp.busicen.rule.flow.action.ActionExecuteBase;
import com.ly.mp.busicen.rule.flow.action.ActionResult;
import com.ly.mp.busicen.rule.flow.action.IAction;
import com.ly.mp.busicen.rule.flow.action.IActionExecute;
import com.ly.mp.busicen.rule.flow.action.IActionResult;
import com.ly.mp.busicen.rule.flow.action.IActionResult.Signal;
import com.ly.mp.busicen.rule.flow.filter.FlowFilter;
import com.ly.mp.busicen.rule.flow.filter.FlowFilterBase;
import com.ly.mp.busicen.rule.flow.filter.FlowFilterContainer;
import com.ly.mp.busicen.rule.flow.filter.FlowInvocation;
import com.ly.mp.busicen.rule.flow.filter.FlowResult;
import com.ly.mp.busicen.rule.flow.filter.FlowResultImpl;

/**
 * 集合调用过滤器，用于集合操作转单元操作
 * @author ly-liuweisong
 *
 */
@Component
public class CollectionFilter extends FlowFilterBase {
	
	private final Logger log = LoggerFactory.getLogger(CollectionFilter.class);
	
	@Autowired
	FlowFilterContainer flowFilterContainer;
		

	@SuppressWarnings("unchecked")
	@Override
	public FlowResult doInvoke(FlowInvocation invocation) {
		String paramStr = splitXKH(invocation.statement());
		
		IDataVolume data = invocation.dataVolume();
		Object extData = FlowSpelUtil.spelGetData(invocation.action(), invocation.action().extKey());
		//如果参数为空或者不是集合
		if (extData==null || !(extData instanceof Iterable)) {
			log.info("流程【{}】节点【{}】数据为空或非迭代类型，不进行迭代处理",invocation.flowVolume().flow(),invocation.action().action());
			return invocation.invoker().invoke(invocation);
		}
		log.info("流程【{}】节点【{}】开始迭代处理",invocation.flowVolume().flow(),invocation.action().action());
		String filterStatement = invocation.action().filter();
		List<String> actionFilter = Stream.of(filterStatement.split(";")).filter(m -> !StringUtils.isEmpty(m))
				.collect(Collectors.toList());
		Iterator<String> iterator = actionFilter.iterator();
		while (iterator.hasNext()) {
			String temp = iterator.next();
			if (splitJKH(temp).equals("collection")) {				
				iterator.remove();
				break;
			}
			iterator.remove();
		}
		actionFilter = IteratorUtils.toList(iterator);
		actionFilter.add("<execution>");
		
		boolean withIndex=false;
		String indexFormater = null;
		String indexZone = null;
		String resultZone = null;
		String breakPreCondition = null;
		String breakPostCondition = null;
		String continueCondition = null;
		String dataTempZone = null;
		if (!StringUtils.isEmpty(paramStr)) {
			Map<String, String> params = splitParam(paramStr);
			if (!StringUtils.isEmpty(params.get("index"))) {
				withIndex = true;
				indexZone=params.get("index");
				indexFormater=params.get("indexformat");
				log.info("流程【{}】节点【{}】迭代处理设置索引临时节点为【{}】，格式化为【{}】",invocation.flowVolume().flow(),invocation.action().action(),
						indexZone,indexFormater==null?"":indexFormater);
			}
			resultZone = params.get("result");
			breakPreCondition = params.get("breakpre");
			breakPostCondition = params.get("breakpost");
			continueCondition = params.get("continue");
			dataTempZone = params.get("datatemp");
		}
		
		if (!StringUtils.isEmpty(dataTempZone)) {
			log.info("流程【{}】节点【{}】设置节点迭代数据到临时节点【{}】",invocation.flowVolume().flow(),invocation.action().action(),dataTempZone);
			FlowSpelUtil.spelSetData(invocation.action(), dataTempZone, extData);
		}
		
		Iterable<?> listData = (Iterable<?>) extData;
		LinkedList<IActionResult> actionResults = new LinkedList<IActionResult>();
		LinkedList<Object> resultData = new LinkedList<Object>();
		try {
			int index = 0;
			for (Object purdata : listData) {
				if (withIndex) {
					Object formatIndex = formateDecimal(++index, indexFormater);
					FlowSpelUtil.spelSetData(invocation.action(), indexZone, formatIndex);
				}
				//修改总线数据为单个数据
				FlowSpelUtil.spelSetData(invocation.action(), invocation.action().extKey(), purdata);
				//循环跳出pre
				if(!StringUtils.isEmpty(breakPreCondition)) {
					Boolean isBreak = FlowSpelUtil.spelGetData(invocation.action(), breakPreCondition,Boolean.class);
					if (isBreak) {
						log.info("流程【{}】节点【{}】迭代处理中符合跳出条件pre【{}】，跳出迭代处理，后续不执行",invocation.flowVolume().flow(),invocation.action().action(),breakPreCondition);
						break;
					}
				}
				//循环continue
				if(!StringUtils.isEmpty(continueCondition)) {
					Boolean isContinue = FlowSpelUtil.spelGetData(invocation.action(), continueCondition,Boolean.class);
					if (isContinue) {
						log.info("流程【{}】节点【{}】迭代处理中符合跳过此次循环【{}】，跳出此次处理，继续执行",invocation.flowVolume().flow(),invocation.action().action(),continueCondition);
						continue;
					}
				}
				IActionResult actionResultTemp = doFilter(purdata, actionFilter, invocation).actionResult();				
				actionResults.add(actionResultTemp);
				resultData.add(actionResultTemp.data());
				if (actionResultTemp.signal()==Signal.BREAK||actionResultTemp.signal()==Signal.EXCPT) {
					log.info("流程【{}】节点【{}】迭代处理中出现了中断或者异常，迭代处理停止",invocation.flowVolume().flow(),invocation.action().action());
					break;
				}
				//循环跳出post
				if(!StringUtils.isEmpty(breakPostCondition)) {
					Boolean isBreak = FlowSpelUtil.spelGetData(invocation.action(), breakPostCondition,Boolean.class);
					if (isBreak) {
						log.info("流程【{}】节点【{}】迭代处理中符合跳出条件post【{}】，跳出迭代处理，后续不执行",invocation.flowVolume().flow(),invocation.action().action(),breakPostCondition);
						break;
					}
				}
			}
			if (!StringUtils.isEmpty(resultZone)) {
				log.info("流程【{}】节点【{}】设置节点迭代结果到【{}】",invocation.flowVolume().flow(),invocation.action().action(),resultZone);
				FlowSpelUtil.spelSetData(invocation.action(), resultZone, resultData);
			}
		} catch (Exception e) {
			ActionResult actionResult = ActionResult.create();
			actionResult.signal(Signal.EXCPT);
			actionResult.excpt(e);
			actionResult.action(invocation.action().action());
			actionResult.msg(invocation.action().msg());
			actionResult.data(null);
			FlowResultImpl flowResultImpl = new FlowResultImpl();
			flowResultImpl.setActionResult(actionResult);
			flowResultImpl.setException(e);
			return flowResultImpl;
		}finally {
			//还原数据
			FlowSpelUtil.spelSetData(invocation.action(), invocation.action().extKey(), listData);
		}
		//结果写到总线
		data.ext().put(invocation.action().action(), actionResults);
		ActionResult actionResult =null;
		if (!actionResults.isEmpty()) {
			actionResult = (ActionResult) actionResults.getLast();
			//动作节点结果数据修改
			actionResult.setData(actionResults);
		}else {
			log.info("流程【{}】节点【{}】迭代数据集合为空，生成默认返回",invocation.flowVolume().flow(),invocation.action().action());
			actionResult = ActionResult.create();
			actionResult.signal(Signal.CONTINUE);
			actionResult.action(invocation.action().action());
			actionResult.msg(invocation.action().msg());
			actionResult.data(null);			
			actionResult.nextAction(ActionExecuteBase.defaultNextAction(invocation.action(), null));
		}
		//过滤器结果生成
		FlowResultImpl flowResultImpl = new FlowResultImpl();
		flowResultImpl.setActionResult(actionResult);
		return flowResultImpl;
	}
	
	
	
	private FlowResult doFilter(Object purdata,List<String> actionFilter,FlowInvocation invocation) {
		//调用后续过滤器
		Queue<String> filterQue = new LinkedBlockingQueue<String>();
		filterQue.addAll(actionFilter);
		FlowInvocation flowInvocation = new FlowInvocation() {

			String statement = "";

			@Override
			public FlowFilter invoker() {
				statement = filterQue.remove();
				return flowFilterContainer.filter(splitJKH(statement));
			}

			@Override
			public String statement() {
				return statement;
			}

			@Override
			public IActionExecute execution() {
				return invocation.execution();
			}

			@Override
			public IDataVolume dataVolume() {
				return invocation.dataVolume();
			}
			
			@Override
			public IFlowVolume flowVolume() {
				return invocation.flowVolume();
			};

			@Override
			public IAction action() {
				return invocation.action();
			}
		};
		return flowInvocation.invoker().invoke(flowInvocation);		
	}
	
	private Object formateDecimal(int index,String format) {
		if (!StringUtils.isEmpty(format)) {
			DecimalFormat df = new DecimalFormat(format);
			return df.format(index);
		}
		return index;
	}
	

	@Override
	public String fileterName() {
		return "collection";
	}
}
